﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection.Emit;
using System.Reflection;

namespace Pixysoft.Framework.Reflection.Controller
{
    class BeanMapFactory
    {
        internal delegate void GetBeanHandler(object target, IBeanMap beanMap);

        internal delegate void SetBeanHandler(object target, IBeanMap beanMap);

        internal static GetBeanHandler CreateBeanMapGetHandler(Type targetType)
        {
            DynamicMethod dynamicMethod = new DynamicMethod("D" + Guid.NewGuid().ToString().Replace("-", "_"),
                MethodAttributes.Static | MethodAttributes.Public,
                CallingConventions.Standard, typeof(void), new Type[] { typeof(object), typeof(IBeanMap) }, typeof(BeanMapFactory), true);

            ILGenerator generator = dynamicMethod.GetILGenerator();

            foreach (PropertyInfo info in targetType.GetProperties())
            {
                if (!info.CanRead)
                    continue;

                OpCodesFactory.LdArgument(generator, 1);
                OpCodesFactory.LdString(generator, info.Name);
                OpCodesFactory.LdArgument(generator, 0);
                OpCodesFactory.CallVirtMethod(generator, info.GetGetMethod());
                OpCodesFactory.BoxIfNeeded(generator, info.PropertyType);
                OpCodesFactory.CallVirtMethod(generator, typeof(IBeanMap).GetMethod("SetValue"));
            }

            OpCodesFactory.ReturnMethod(generator);

            return (GetBeanHandler)dynamicMethod.CreateDelegate(typeof(GetBeanHandler));
        }

        internal static SetBeanHandler CreateBeanMapSetHandler(Type targetType)
        {
            DynamicMethod dynamicMethod = new DynamicMethod("D" + Guid.NewGuid().ToString().Replace("-", "_"),
                MethodAttributes.Static | MethodAttributes.Public,
                CallingConventions.Standard, typeof(void), new Type[] { typeof(object), typeof(IBeanMap) }, typeof(BeanMapFactory), true);

            ILGenerator generator = dynamicMethod.GetILGenerator();

            foreach (PropertyInfo info in targetType.GetProperties())
            {
                if (!info.CanWrite)
                    continue;

                OpCodesFactory.LdArgument(generator, 0);
                OpCodesFactory.LdArgument(generator, 1);
                OpCodesFactory.LdString(generator, info.Name);
                OpCodesFactory.LdType(generator, info.PropertyType);
                OpCodesFactory.CallVirtMethod(generator, typeof(IBeanMap).GetMethod("TryGetValue"));
                if (info.PropertyType.IsValueType)
                    OpCodesFactory.UnboxIfNeeded(generator, info.PropertyType);
                else
                    OpCodesFactory.CastIfNeeded(generator, info.PropertyType);
                OpCodesFactory.CallVirtMethod(generator, info.GetSetMethod());
            }

            OpCodesFactory.ReturnMethod(generator);

            return (SetBeanHandler)dynamicMethod.CreateDelegate(typeof(SetBeanHandler));
        }
    }
}
